/**
 * Copyright 2018 人人开源 http://www.renren.io
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package cn.yi.boot.shiro.config;

import cn.yi.boot.shiro.security.RedisShiroSessionDAO;
import cn.yi.boot.shiro.security.ShiroUtils;
import cn.yi.boot.shiro.security.UserCredentialsMatcher;
import cn.yi.boot.shiro.security.UserRealm;
import cn.yi.boot.shiro.security.filter.KickoutSessionControlFilter;
import cn.yi.boot.shiro.security.filter.UserSessionFilter;
import com.google.common.collect.Maps;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.Map;

/**
 * Shiro的配置文件
 *
 * @author Mark sunlightcs@gmail.com
 * @since 3.0.0 2017-09-27
 */
@Configuration
public class ShiroConfig {

    /**
     * 开启cookie
     *
     * @param cookieTimeout
     * @return
     */
    @Bean("simpleCookie")
    public SimpleCookie simpleCookie(@Value("${shiro.cookie.timeout:-1}") Integer cookieTimeout) {
        SimpleCookie simpleCookie = new SimpleCookie("sid");
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(cookieTimeout);
        return simpleCookie;
    }

    /**
     * session管理器
     *
     * @param globalSessionTimeout
     * @param redisShiroSessionDAO //     * @param validationScheduler
     * @param simpleCookie
     * @return
     */
    @Bean("sessionManager")
    public SessionManager sessionManager(@Value("${shiro.login.session.timeout}") Long globalSessionTimeout,
                                         RedisShiroSessionDAO redisShiroSessionDAO,
//                                         ExecutorServiceSessionValidationScheduler validationScheduler,
                                         SimpleCookie simpleCookie) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //设置session过期时间为1小时(单位：毫秒)，默认为30分钟
        sessionManager.setGlobalSessionTimeout(globalSessionTimeout);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
//        sessionManager.setSessionValidationScheduler(validationScheduler);
        sessionManager.setSessionDAO(redisShiroSessionDAO);

        //开启cookie 不然一直登录不上，remember不起作用
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(simpleCookie);
        return sessionManager;
    }

    @Bean("userCredentialsMatcher")
   public UserCredentialsMatcher setUserCredentialsMatcher(@Value("${shiro.login.error.retryTimes}") Long retryTimes
            , @Value("${shiro.login.error.cacheName}") String cacheName,CacheManager cacheManager) {
        UserCredentialsMatcher credentialsMatcher = new UserCredentialsMatcher();
        credentialsMatcher.setRetryTimes(retryTimes);
        credentialsMatcher.setCacheName(cacheName);
        credentialsMatcher.setCacheManager(cacheManager);
        credentialsMatcher.setHashAlgorithmName(ShiroUtils.hashAlgorithmName);
        credentialsMatcher.setHashIterations(ShiroUtils.hashIterations);
        return credentialsMatcher;
    }

    @Bean("userRealm")
    public UserRealm setUserRealm(@Value("${user.login.cacheName:webLoginCache}") String authenticationCacheName, UserCredentialsMatcher userCredentialsMatcher) {
        UserRealm userRealm = new UserRealm();
        userRealm.setAuthenticationCacheName(authenticationCacheName);
        userRealm.setCredentialsMatcher(userCredentialsMatcher);
        return userRealm;
    }
    /**
     * 定时心跳监测
     * @param interval
     * @param sessionManager
     * @return
     */
   /* @Bean("validationScheduler")
    public ExecutorServiceSessionValidationScheduler validationScheduler(@Value("${shiro.session.timeout}") Integer interval, SessionManager sessionManager) {
        ExecutorServiceSessionValidationScheduler validationScheduler = new ExecutorServiceSessionValidationScheduler();
        validationScheduler.setInterval(interval);
//        validationScheduler.setSessionManager(sessionManager);
        return validationScheduler;
    }*/


    /**
     * 认证管理
     *
     * @param userRealm
     * @param sessionManager
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setSessionManager(sessionManager);

        return securityManager;
    }


    /**
     * 过滤
     *
     * @param securityManager
     * @param userFilter
     * @param kickoutFilter
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager,
                                              UserSessionFilter userFilter,
                                              KickoutSessionControlFilter kickoutFilter) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setLoginUrl("/toLogin");
        shiroFilter.setUnauthorizedUrl("/");
        Map<String, Filter> filters = Maps.newHashMap();
        filters.put("userFilter", userFilter);
        filters.put("kickoutFilter", kickoutFilter);
        shiroFilter.setFilters(filters);
        shiroFilter.setFilterChainDefinitions("/** = kickoutFilter,userFilter");

       /* Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/swagger/**", "anon");
        filterMap.put("/v2/api-docs", "anon");
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/swagger-resources/**", "anon");

        filterMap.put("/statics/**", "anon");
        filterMap.put("/login.html", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/favicon.ico", "anon");
        filterMap.put("/captcha.jpg", "anon");
        filterMap.put("/**", "authc");
        shiroFilter.setFilterChainDefinitionMap(filterMap);
*/
        return shiroFilter;
    }

    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public MethodInvokingFactoryBean invoke(SecurityManager securityManager) {
        MethodInvokingFactoryBean invokingFactoryBean = new MethodInvokingFactoryBean();
        invokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        invokingFactoryBean.setArguments(securityManager);
        return invokingFactoryBean;
    }
}
